package site.brood.common.security.feign;


import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import site.brood.common.core.ip.IpUtil;
import site.brood.common.core.str.StrUtil;
import site.brood.common.core.web.ServletUtil;
import site.brood.common.core.constant.Constants;
import site.brood.common.security.util.SecurityUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * feign 请求拦截器
 * 外部网络请求流程：
 * 外部网络请求header中带jwt格式token
 * 一、经过网关服务
 * 在网关拦截器中，从jwt中取出userId,userName,userKey，转存到header中，转发给业务微服务
 * 二、内部微服务
 * 1)在请求头拦截器中，取出header中的userId,userName,userKey，转存到TransmittableThreadLocal对象中
 * 通过userKey从redis中取出用户详情，以userKey为key存入TransmittableThreadLocal对象中
 * 2)调用内部feign服务(例如：日志服务)
 * 在feign拦截器中，从主线程request中或TransmittableThreadLocal对象中取出userId,userName,userKey，转存到feign header中
 * 三、日志服务
 * 1)在鉴权注解(RequiresPermissions)的AOP回调中，从TransmittableThreadLocal对象中取出userId,userName,userKey
 * 通过userKey从TransmittableThreadLocal对象中取出用户详情
 * 通过用户详情中的权限列表，与鉴权注解中要求的权限数组比对
 * 鉴权失败抛出鉴权失败异常
 * 四、全局异常AOP
 * 将鉴权失败异常转为AjaxJson对象写入网络response
 */
@Component
public class FeignRequestInterceptor implements RequestInterceptor
{
    @Override
    public void apply(RequestTemplate requestTemplate)
    {
        //注意：异步发起feign网络请求获取不到主线程中的httpRequest对象
        //feign默认使用主线程发网络请求，也可通过@Async异步发feign网络请求(例如：日志服务不需要阻塞主线程)
        //异步feign取不到主线程的httpRrequest对象，解决办法：
        //可通过线程池共享对象(TransmittableThreadLocal)记录主线程httpRequest中的header，共享给异步线程中的FeignRequestInterceptor
        //在全局请求头拦截器(HeaderInterceptor)中转存主线程httpRequest的header到线程池共享对象(TransmittableThreadLocal)中
        HttpServletRequest httpServletRequest = ServletUtil.getRequest();
        if (StrUtil.isNotNull(httpServletRequest))
        {
            Map<String, String> headers = ServletUtil.getHeaders(httpServletRequest);
            // 把外部网络请求头转存到feign请求头中，供微服务鉴权使用
            // 网关转发的请求头中有用户信息
            String userId = headers.get(Constants.INNER_HEADER_USER_ID);
            String username = headers.get(Constants.INNER_HEADER_USER_NAME);
            String authentication = headers.get(Constants.OUTTER_HEAD_TOKEN);
            String userKey = headers.get(Constants.INNER_HEADER_USER_CACHE_KEY);
            saveHeads(userId, username, userKey, authentication, requestTemplate);
        }else{
            String userId = SecurityUtil.getUserId();
            String username = SecurityUtil.getUsername();
            String authentication = SecurityUtil.getTokenWithPrefix();
            String userKey = SecurityUtil.getUserKey();
            saveHeads(userId, username, userKey, authentication, requestTemplate);
        }
        // 存客户端IP
        requestTemplate.header("X-Forwarded-For", IpUtil.getIpAddr(ServletUtil.getRequest()));
    }

    //将用户信息的key存入feign的header
    private void saveHeads(String userId, String username, String userKey, String authentication,RequestTemplate requestTemplate){
        if (StrUtil.isNotEmpty(userId))
        {
            requestTemplate.header(Constants.INNER_HEADER_USER_ID, userId);
        }
        if (StrUtil.isNotEmpty(username))
        {
            requestTemplate.header(Constants.INNER_HEADER_USER_NAME, username);
        }
        if (StrUtil.isNotEmpty(userKey))
        {
            requestTemplate.header(Constants.INNER_HEADER_USER_CACHE_KEY, userKey);
        }
        if (StrUtil.isNotEmpty(authentication))
        {
            requestTemplate.header(Constants.OUTTER_HEAD_TOKEN, authentication);
        }
    }
}
